home *** CD-ROM | disk | FTP | other *** search
/ BBS in a Box 3 / BBS in a box - Trilogy III.iso / Files / Prog / U-Z / VideoToolBox Folder / VideoToolboxSources / GDFrameRate.c < prev    next >
Encoding:
Text File  |  1993-03-15  |  13.4 KB  |  387 lines  |  [TEXT/KAHL]

  1. /*
  2. GDFrameRate.c
  3.  
  4. (For background, read "Video synch" on the VideoToolbox disk, and run TimeVideo,
  5. which reports the results of running these routines.)
  6.  
  7. double GDFrameRate(GDHandle device) measures the frame rate of a video device in
  8. Hz. (A NULL "device" argument to this routine, or any of the routines below,
  9. requests use of the System VBL interrupt, which normally runs at 60.15 Hz.) It
  10. times by counting VBL interrupts (discarding any spurious ones, to deal with 
  11. the problem described in the next paragraph).
  12.  
  13. double GDVBLRate(GDHandle device) measures the rate of VBL interrupts generated
  14. by a video device in Hz. According to Apple's Designing Cards and Drivers book
  15. and other documentation, the video driver and card are supposed to generate one
  16. VBL interrupt per frame. However, many don't. E.g. Apple's 4•8 and 8•24 video
  17. cards issue several interrupts per frame. Read the "Video synch" file.
  18.  
  19. double GDMovieSize(GDHandle device,int quickly) measures what fraction of the
  20. screen you can fill with a real-time movie (a new image on each frame), using
  21. CopyBitsQuickly (if quickly!=0) or CopyBits (if quickly==0) to copy from memory
  22. to video card. At one time CopyBitsQuickly() was much faster than CopyBits(),
  23. but my latest measurements, using GDMovieRate, indicate that there is no longer
  24. any difference in speed. However, CopyBitsQuickly ignores the color tables and
  25. CopyBits uses them. Of course, when you're showing movies you don't want to
  26. waste time with color tables, so in GDMovieRate() I made a PixMap that shared
  27. the device's color table. For reasons that I don't understand even in that case
  28. using CopyBits to copy from and then back to the screen doesn't always preserve
  29. the original colors.
  30.  
  31. double GDMovieRate(GDHandle device,int quickly) measures the rate (images/s) at 
  32. which you can show a full-screen movie.
  33.  
  34. GDFramesPerClutUpdate is obsolete. Use GDTimeClut.c instead.
  35.  
  36. GDClutUpdateRate is obsolete. Use GDTimeClut.c instead.
  37.  
  38. GDTimeClutUpdate is obsolete. Use GDTimeClut.c instead.
  39.  
  40. HISTORY:
  41. 8/22/92 dgp    wrote 'em.
  42. 8/26/92    dgp    added clutEntries argument
  43. 8/28/92    dgp    updated to use new reentrant Timer.c
  44. 9/11/92    dgp enhanced GDFrameRate() to discard bogus VBL interrupts and to return
  45.             true frame rate, as suggested by Raynald Comtois. Added GDVBLRate(), 
  46.             which corresponds to the old GDFrameRate().
  47. 9/15/92    dgp    GDMovieRate() now asks the video driver what mode we're in, just in case
  48.             QuickDraw's been fooled.
  49. 9/17/92    dgp    Added second argument to GDMovieRate() to select CopyBitsQuickly vs CopyBits.
  50. 10/5/92    dgp    fixed bug in GDMovieRate() that caused crashes or noop when testing other 
  51.             than the main device.
  52. 10/9/92    dgp    now actually initialize the linearTable for direct clut.
  53. 10/10/92 dgp Squeezed out extra space from rowBytes in GDMovieRate() so as not to show
  54.             garbage. Now use Temporary memory if there isn't enough memory in the application 
  55.             heap to show a full-screen movie. Show movie for 3 seconds. 
  56. 10/13/92 dgp In response to a report from Tom Busey, that frames were going uncounted
  57.             during the clut timing, which seems to be a problem with some video drivers,
  58.             GDFramesPerClutUpdate() now double checks the timing in secs, and if
  59.             it finds a discrepancy, prints a warning to the screen and reports a
  60.             corrected values based on the timing in secs. This should be more reliable.
  61. 10/13/92 dgp Fixed error in printf in GDFrameRate().
  62. 11/23/92 dgp Set nominalBits equal to pixelSize instead of Log2L(ctsize).
  63. 12/30/92 dgp Commented out warning from GDFramesPerClutUpdate().
  64.              •Check for SetEntries error in the clut timing routines, and return
  65.              NAN in that case. •Use trial and error to determine clut size.
  66. 1/4/92    dgp    GDTimeClutUpdate now returns NAN on GDSetEntries error.
  67. 1/6/92    dgp    Fixed computation of linearTable, so CLUT is preserved by GDTimeClutUpdate.
  68. 1/11/93    dgp    Enhanced GDMovieRate() to work even when Color QuickDraw is absent, by 
  69.             calling the new routine GDMovieRateNoColorQuickDraw().
  70.             Check returned Ptr from NewTimer() to make sure it's ok; will be NULL
  71.             if computer only has Standard Time Manager.
  72. 1/24/93    dgp    Reduced timing interval from 3 to 1 s for movies.
  73. 3/15/93    dgp    Fixed portRect clipping error in GDMovieRateNoColorQuickDraw.
  74. */
  75. #include "VideoToolbox.h"
  76. #include <assert.h>
  77. #include <math.h>
  78. #if THINK_C
  79.     #include <LoMem.h>
  80. #else
  81.     short MBarHeight : 0xBAA;
  82. #endif
  83. double GDMovieRateNoColorQuickDraw(int quickly);
  84. #define SHOW_MOVIE_WINDOW 0    // A matter of taste, but I prefer not to show it.
  85. #define FRAMES 10            // fewer for speed, more for accuracy
  86.  
  87. double GDFrameRate(GDHandle device)
  88. {
  89.     VBLTaskAndA5 vblData;
  90.     Timer *timer;
  91.     register long frames;
  92.     int error;
  93.     double s;
  94.     
  95.     timer=NewTimer();
  96.     if(timer==NULL)return NAN;            // lacks Revised Time Manager.
  97.     vblData.subroutine=NULL;
  98.     error=VBLInstall(&vblData,device,FRAMES);
  99.     if(error)PrintfExit("GDFrameRate: VBLInstall: error %d\n",error);
  100.     vblData.vbl.vblCount=1;                // Enable interrupt service routine
  101.     frames=vblData.framesDesired-2;
  102.     while(vblData.framesLeft>frames);    // wait for second frame
  103.     StartTimer(timer);
  104.     while(vblData.framesLeft);            // wait for last frame
  105.     s=StopTimerSecs(timer);
  106.     VBLRemove(&vblData);
  107.     DisposeTimer(timer);
  108.     return frames/s;
  109. }
  110.  
  111. double GDVBLRate(GDHandle device)
  112. {
  113.     VBLTaskAndA5 vblData;
  114.     Timer *timer;
  115.     register long frames;
  116.     int error;
  117.     double s;
  118.     
  119.     timer=NewTimer();
  120.     if(timer==NULL)return NAN;            // lacks Revised Time Manager.
  121.     vblData.subroutine=SimpleVBLSubroutine;
  122.     error=VBLInstall(&vblData,device,FRAMES);
  123.     if(error)PrintfExit("GDVBLRate: VBLInstall: error %d\n",error);
  124.     vblData.vbl.vblCount=1;                // Enable interrupt service routine
  125.     frames=vblData.framesDesired-2;
  126.     while(vblData.framesLeft>frames);    // wait for second frame
  127.     StartTimer(timer);
  128.     while(vblData.framesLeft);            // wait for last frame
  129.     s=StopTimerSecs(timer);
  130.     VBLRemove(&vblData);
  131.     DisposeTimer(timer);
  132.     return frames/s;
  133. }
  134.  
  135. double GDMovieSize(GDHandle device,int quickly)
  136. {
  137.     return GDMovieRate(device,quickly)/GDFrameRate(device);
  138. }
  139.  
  140. double GDMovieRate(GDHandle device,int quickly)
  141. {
  142.     Timer *timer;
  143.     register long image;
  144.     long images;
  145.     OSErr error;
  146.     double s=NAN,fractionOfFrame=NAN;
  147.     PixMap **pm;
  148.     unsigned long bytes;
  149.     GDHandle oldDevice;
  150.     WindowPtr window,oldPort;
  151.     static Rect r,rLocal;
  152.     Ptr oldBaseAddr;
  153.     short mode;
  154.     short nominalBits,trueBits;
  155.     Handle saveSpace,bufferHandle;
  156.     long osAttr;
  157.     int tempMem;
  158.     
  159.     if(!QD8Exists())return GDMovieRateNoColorQuickDraw(quickly);
  160.     oldDevice=GetGDevice();
  161.     SetGDevice(device);
  162.     pm=NewPixMap();
  163.     SetGDevice(oldDevice);
  164.     if(pm==NULL)goto done0;
  165.     HLock((Handle)pm);
  166.     // The color table is needed for CopyBits(); CopyBitsQuickly doesn't care.
  167.     (**pm).pmTable=(**(**device).gdPMap).pmTable;    // share device's color table
  168.     if(device==GetMainDevice())(**pm).bounds.top+=MBarHeight;
  169.     if(SHOW_MOVIE_WINDOW){
  170.         (**pm).bounds.top+=19;    // Allow room for window title
  171.         InsetRect(&(**pm).bounds,32,32);
  172.     }
  173.     bufferHandle=NULL;
  174.     bytes=(**pm).bounds.right-(**pm).bounds.left;
  175.     bytes*=(**pm).pixelSize;
  176.     bytes=((bytes+31)/32)*4;    // convert bits to bytes, rounding up to multiple of 4
  177.     (**pm).rowBytes &= ~0x3fff;
  178.     (**pm).rowBytes |= bytes;
  179.     Gestalt(gestaltOSAttr,&osAttr);
  180.     tempMem=osAttr & 1L<<gestaltTempMemSupport;
  181.     while(1){
  182.         bytes=(**pm).rowBytes & 0x3fff;
  183.         bytes*=(**pm).bounds.bottom-(**pm).bounds.top;
  184.         if(bytes==0)goto done1;
  185.         saveSpace=NewHandle(2000);    // save some space
  186.         bufferHandle=NewHandle(bytes+1200);    // extra is for drifting
  187.         if(saveSpace!=NULL)DisposeHandle(saveSpace);
  188.         if(bufferHandle==NULL && tempMem)bufferHandle=TempNewHandle(bytes+1200,&error);
  189.         if(bufferHandle!=NULL)break;
  190.         // Halve the window's height before trying again
  191.         (**pm).bounds.bottom=(**pm).bounds.top+((**pm).bounds.bottom-(**pm).bounds.top)/2;
  192.     }
  193.     HLock(bufferHandle);
  194.     (**pm).baseAddr=*bufferHandle;
  195.     GetPort(&oldPort);
  196.     r=(**pm).bounds;
  197.     window=NewCWindow(NULL,&r,"\pmovie",0,0,(WindowPtr)-1,0,0);    // don't show it yet
  198.     if(window==NULL)goto done2;
  199.     timer=NewTimer();
  200.     if(timer==NULL)goto done2;        // lacks Revised Time Manager.
  201.     SetPort(window);
  202.     HLock((Handle)((CWindowPtr)window)->portPixMap);
  203.     SetGDevice(device);
  204.     rLocal=r;
  205.     GlobalToLocalRect(&rLocal);
  206.     StartTimer(timer);
  207.     if(quickly)CopyBitsQuickly((BitMap *)*((CWindowPtr)window)->portPixMap,(BitMap *)*pm
  208.         ,&rLocal,&r,srcCopy,NULL);    // copy screen to memory
  209.     else{
  210.         CopyBits((BitMap *)*((CWindowPtr)window)->portPixMap,(BitMap *)*pm
  211.         ,&rLocal,&r,srcCopy,NULL);    // copy screen to memory
  212.         if(error=QDError()){
  213.             printf("GDMovieRate: CopyBits generated QuickDraw error %d\n",error);
  214.             goto done3;
  215.         }
  216.     }
  217.     s=StopTimerSecs(timer);    // rough estimate of time for one image
  218.     if(SHOW_MOVIE_WINDOW || !quickly)ShowWindow(window);// CopyBits won't copy to a hidden window
  219.     StartTimer(timer);
  220.     oldBaseAddr=(**pm).baseAddr;
  221.     // Let's time for this many seconds.
  222.     images=ceil(1.0/s);
  223.     for(image=images;image>0;image--){
  224.         if(image==1)(**pm).baseAddr=oldBaseAddr;
  225.         else (**pm).baseAddr+=4;    // Drift image to prove it's a movie
  226.         // We drift by multiples of 4 bytes because long-aligned copying is faster.
  227.         if(quickly)CopyBitsQuickly((BitMap *)*pm,(BitMap *)*((CWindowPtr)window)->portPixMap
  228.             ,&r,&rLocal,srcCopy,NULL);    // copy memory to screen
  229.         else CopyBits((BitMap *)*pm,(BitMap *)*((CWindowPtr)window)->portPixMap
  230.             ,&r,&rLocal,srcCopy,NULL);    // copy memory to screen
  231.     }
  232.     s=StopTimerSecs(timer);
  233.     fractionOfFrame=(long)(r.bottom-r.top)*(long)(r.right-r.left);
  234.     r=(**(**device).gdPMap).bounds;
  235.     fractionOfFrame/=(long)(r.bottom-r.top)*(long)(r.right-r.left);
  236.     if(1){
  237.         // Ask the video driver what mode we're in. In case QuickDraw's been fooled.
  238.         GDGetMode(device,&mode,NULL,NULL);
  239.         nominalBits=(**(**device).gdPMap).pixelSize;
  240.         trueBits=1<<(mode&7);
  241.         fractionOfFrame*=(double)nominalBits/trueBits;
  242.     }
  243.     DisposeTimer(timer);
  244. done3:
  245.     SetPort(oldPort);
  246.     DisposeWindow(window);
  247. done2:
  248.     DisposHandle(bufferHandle);
  249. done1:
  250.     (**pm).pmTable=NULL;
  251.     DisposePixMap(pm);
  252. done0:
  253.     SetGDevice(oldDevice);
  254.     return images*fractionOfFrame/s;
  255. }
  256.  
  257. double GDMovieRateNoColorQuickDraw(int quickly)
  258. {
  259.     Timer *timer;
  260.     register long image;
  261.     long images;
  262.     OSErr error=0;
  263.     double s=NAN,fractionOfFrame=NAN;
  264.     BitMap bitmap;
  265.     unsigned long bytes;
  266.     Rect r;
  267.     Ptr oldBaseAddr;
  268.     Handle saveSpace,bufferHandle=NULL;
  269.     long osAttr;
  270.     int tempMem;
  271.     char string[100];
  272.     GrafPort portRec,*port=&portRec,*oldPort;
  273.     
  274.     GetPort(&oldPort);
  275.     OpenPort(port);
  276.     SetPort(port);
  277.     bitmap=port->portBits;
  278.     bytes=bitmap.bounds.right-bitmap.bounds.left;
  279.     bitmap.rowBytes=((bytes+31)/32)*4;    // convert bits to bytes, round up to mult of 4
  280.     Gestalt(gestaltOSAttr,&osAttr);
  281.     tempMem=osAttr & 1L<<gestaltTempMemSupport;
  282.     while(1){
  283.         bytes=bitmap.rowBytes & 0x3fff;
  284.         bytes*=bitmap.bounds.bottom-bitmap.bounds.top;
  285.         if(bytes==0)goto done;
  286.         saveSpace=NewHandle(2000);    // save some space
  287.         bufferHandle=NewHandle(bytes+600);    // extra is for drifting
  288.         if(saveSpace!=NULL)DisposeHandle(saveSpace);
  289.         if(bufferHandle==NULL && tempMem)bufferHandle=TempNewHandle(bytes+600,&error);
  290.         if(bufferHandle!=NULL)break;
  291.         // Halve the window's height before trying again
  292.         bitmap.bounds.bottom=bitmap.bounds.top+(bitmap.bounds.bottom-bitmap.bounds.top)/2;
  293.     }
  294.     HLock(bufferHandle);
  295.     bitmap.baseAddr=*bufferHandle;
  296.     timer=NewTimer();
  297.     if(timer==NULL)goto done;    // lacks Revised Time Manager.
  298.     r=bitmap.bounds;
  299.     StartTimer(timer);
  300.     if(quickly)CopyBitsQuickly(&port->portBits,&bitmap
  301.         ,&r,&r,srcCopy,NULL);    // copy screen to memory
  302.     else CopyBits(&port->portBits,&bitmap
  303.         ,&r,&r,srcCopy,NULL);    // copy screen to memory
  304.     s=StopTimerSecs(timer);        // approximate time for one image
  305.     if(!quickly && (error=QDError())){
  306.         printf("GDMovieRate: CopyBits generated QuickDraw error %d\n",error);
  307.         DisposeTimer(timer);
  308.         goto done;
  309.     }
  310.     oldBaseAddr=bitmap.baseAddr;
  311.     images=ceil(1.0/s);    // Let's time for this many seconds.
  312.     StartTimer(timer);
  313.     for(image=images;image>0;image--){
  314.         if(image==1)bitmap.baseAddr=oldBaseAddr;
  315.         else bitmap.baseAddr+=2;    // Drift the image, to prove it's a movie
  316.         if(quickly)CopyBitsQuickly(&bitmap,&port->portBits
  317.             ,&r,&r,srcCopy,NULL);    // copy memory to screen
  318.         else CopyBits(&bitmap,&port->portBits
  319.             ,&r,&r,srcCopy,NULL);    // copy memory to screen
  320.     }
  321.     s=StopTimerSecs(timer);
  322.     DisposeTimer(timer);
  323.     if(!quickly && (error=QDError())){
  324.         printf("GDMovieRate: CopyBits generated QuickDraw error %d\n",error);
  325.         DisposeTimer(timer);
  326.         goto done;
  327.     }
  328.     fractionOfFrame=(long)(r.bottom-r.top)*(long)(r.right-r.left);
  329.     r=port->portBits.bounds;
  330.     fractionOfFrame/=(long)(r.bottom-r.top)*(long)(r.right-r.left);
  331. done:
  332.     SetPort(oldPort);
  333.     ClosePort(port);
  334.     if(bufferHandle==NULL){
  335.         printf("GDMovieRate: Not enough memory!\n");
  336.         return NAN;
  337.     }
  338.     DisposeHandle(bufferHandle);
  339.     return images*fractionOfFrame/s;
  340. }
  341.  
  342. double TickRate(void)
  343. {
  344.     Timer *timer;
  345.     double s;
  346.     long t;
  347.     
  348.     timer=NewTimer();
  349.     if(timer==NULL)return NAN;            // lacks Revised Time Manager.
  350.     Delay(1,&t);
  351.     StartTimer(timer);
  352.     Delay(FRAMES,&t);
  353.     s=StopTimerSecs(timer);
  354.     DisposeTimer(timer);
  355.     return FRAMES/s;
  356. }
  357.  
  358. double GDFramesPerClutUpdate(GDHandle device,long clutEntries)
  359. // OBSOLETE. Use GDTimeClut() instead.
  360. {
  361.     double frames;
  362.     
  363.     GDTimeClut(device,GDSetEntries,clutEntries,NULL,&frames,NULL,NULL);
  364.     return frames;
  365. }
  366.  
  367. double GDClutUpdateRate(GDHandle device,long clutEntries)
  368. // OBSOLETE. Use GDTimeClut() instead.
  369. {
  370.     double s;
  371.     
  372.     GDTimeClut(device,GDSetEntries,clutEntries,&s,NULL,NULL,NULL);
  373.     return 1.0/s;
  374. }
  375.  
  376. void GDTimeClutUpdate(GDHandle device,long clutEntries,double *framesPtr,double *sPtr)
  377. // OBSOLETE. Use GDTimeClut() instead.
  378. {
  379.     double missingFrames;
  380.  
  381.     GDTimeClut(device,GDSetEntries,clutEntries,sPtr,framesPtr,&missingFrames,NULL);
  382.     
  383.     // We're trying replicate the old behavior, so remove the correction.
  384.     if(framesPtr!=NULL)*framesPtr-=missingFrames;
  385. }
  386.  
  387.